Skip to content

ci: publish releases to npm with trusted publishing - #10

Merged
Bre77 merged 2 commits into
mainfrom
fm/tsfa-ci-publish-workflow
Aug 1, 2026
Merged

ci: publish releases to npm with trusted publishing#10
Bre77 merged 2 commits into
mainfrom
fm/tsfa-ci-publish-workflow

Conversation

@Bre77

@Bre77 Bre77 commented Aug 1, 2026

Copy link
Copy Markdown
Member

Intent

Add a GitHub Actions publish workflow (.github/workflows/publish.yml) to node-tesla-fleet-api so the tesla-fleet-api npm package can be published from CI, since there is currently NO publish path (the old CI publish workflow was deleted in 2024) and 0.2.1 is sitting on main unpublished. Requirements: use npm trusted publishing (OIDC) - id-token: write permission, NO NPM_TOKEN secret - matching how the rest of the Teslemetry fleet (typescript-teslemetry, tesla-protocol) publishes; the captain is separately configuring npm trusted publishing on the npm package side. Modeled the trigger on this repo's own pre-2024 deleted publish.yaml convention (on: release: types: [published]) rather than the fleet's push-to-main + changesets flow, deliberately, because this repo is a single package with no changesets/monorepo tooling configured - copying the changesets-based flow verbatim would require bootstrapping changesets from scratch, which is out of scope. Workflow builds via 'npx tsc' (uses this repo's own pnpm + tsc build, node 22 / pnpm 10 to match the existing ci.yml) before 'npm publish --provenance --access public', so dist/ (including dist/tariff.js, dist/tariff.d.ts, dist/types/site_info.*) is guaranteed fresh before publish. Did NOT attempt to actually publish - that only happens once this merges AND the captain enables npm trusted publishing for this package. Also updated AGENTS.md's existing note about there being 'no CI publish path' to describe the new workflow instead, since that note would otherwise go stale.

What Changed

  • Add a GitHub Release-triggered workflow that installs dependencies, builds with TypeScript, and publishes the package to npm with provenance.
  • Configure npm trusted publishing through OIDC, including the permissions required for repository checkout and token issuance, without an NPM_TOKEN secret.
  • Update project agent guidance to document the restored CI publishing path and release prerequisites.

Risk Assessment

✅ Low: The publish workflow is well-bounded, satisfies the stated release-triggered OIDC publishing requirements, and now grants both repository checkout and identity-token permissions at job scope.

Testing

After installing locked dependencies to resolve the initially absent compiler, I exercised the workflow’s install/build/package path, verified the tarball contents and workflow contract, captured evidence, removed transient dependencies, and found no issues; no real npm publication was attempted, as required.

Evidence: Publish package evidence

The dry-run package is tesla-fleet-api@0.2.1 and includes fresh tariff and site_info JavaScript/declaration outputs.

{
  "package": "tesla-fleet-api",
  "version": "0.2.1",
  "filename": "tesla-fleet-api-0.2.1.tgz",
  "files": [
    {
      "path": "dist/tariff.js",
      "included": true
    },
    {
      "path": "dist/tariff.d.ts",
      "included": true
    },
    {
      "path": "dist/types/site_info.js",
      "included": true
    },
    {
      "path": "dist/types/site_info.d.ts",
      "included": true
    }
  ],
  "totalFiles": 102
}
Evidence: Workflow contract evidence

Confirms the release trigger, OIDC permission, no npm-token secret reference, expected tool versions, build ordering, and provenance publish command.

{
  "releasePublishedTrigger": true,
  "oidcPermission": true,
  "noNpmTokenSecretReference": true,
  "node22": true,
  "pnpm10": true,
  "frozenInstall": true,
  "buildBeforePublish": true,
  "provenancePublicPublish": true
}
Evidence: Complete npm pack dry-run manifest
[
  {
    "id": "tesla-fleet-api@0.2.1",
    "name": "tesla-fleet-api",
    "version": "0.2.1",
    "size": 94945,
    "unpackedSize": 527441,
    "shasum": "00cc50b7c8688155e51c30b77d306fc7f5b8e294",
    "integrity": "sha512-WR8D+pEzm9in5GxoO94auF58JFwf9l3zzrFMCSBm1Dp0YIhP5D/8Pk1eSx7mA84AAbu/VphVjUJyHHEvfxNmHw==",
    "filename": "tesla-fleet-api-0.2.1.tgz",
    "files": [
      {
        "path": "README.md",
        "size": 322,
        "mode": 420
      },
      {
        "path": "dist/charging.d.ts",
        "size": 1784,
        "mode": 420
      },
      {
        "path": "dist/charging.js",
        "size": 2117,
        "mode": 420
      },
      {
        "path": "dist/charging.js.map",
        "size": 853,
        "mode": 420
      },
      {
        "path": "dist/commands.d.ts",
        "size": 16654,
        "mode": 420
      },
      {
        "path": "dist/commands.js",
        "size": 44991,
        "mode": 420
      },
      {
        "path": "dist/commands.js.map",
        "size": 27785,
        "mode": 420
      },
      {
        "path": "dist/energy.d.ts",
        "size": 6066,
        "mode": 420
      },
      {
        "path": "dist/energy.js",
        "size": 7135,
        "mode": 420
      },
      {
        "path": "dist/energy.js.map",
        "size": 2463,
        "mode": 420
      },
      {
        "path": "dist/energyspecific.d.ts",
        "size": 4561,
        "mode": 420
      },
      {
        "path": "dist/energyspecific.js",
        "size": 5102,
        "mode": 420
      },
      {
        "path": "dist/energyspecific.js.map",
        "size": 1831,
        "mode": 420
      },
      {
        "path": "dist/helpers.d.ts",
        "size": 128,
        "mode": 420
      },
      {
        "path": "dist/helpers.js",
        "size": 284,
        "mode": 420
      },
      {
        "path": "dist/helpers.js.map",
        "size": 447,
        "mode": 420
      },
      {
        "path": "dist/index.d.ts",
        "size": 824,
        "mode": 420
      },
      {
        "path": "dist/index.js",
        "size": 778,
        "mode": 420
      },
      {
        "path": "dist/index.js.map",
        "size": 671,
        "mode": 420
      },
      {
        "path": "dist/partner.d.ts",
        "size": 962,
        "mode": 420
      },
      {
        "path": "dist/partner.js",
        "size": 1172,
        "mode": 420
      },
      {
        "path": "dist/partner.js.map",
        "size": 661,
        "mode": 420
      },
      {
        "path": "dist/signing/crypto.d.ts",
        "size": 612,
        "mode": 420
      },
      {
        "path": "dist/signing/crypto.js",
        "size": 1107,
        "mode": 420
      },
      {
        "path": "dist/signing/crypto.js.map",
        "size": 911,
        "mode": 420
      },
      {
        "path": "dist/signing/errors.d.ts",
        "size": 1121,
        "mode": 420
      },
      {
        "path": "dist/signing/errors.js",
        "size": 1548,
        "mode": 420
      },
      {
        "path": "dist/signing/errors.js.map",
        "size": 768,
        "mode": 420
      },
      {
        "path": "dist/signing/session.d.ts",
        "size": 3240,
        "mode": 420
      },
      {
        "path": "dist/signing/session.js",
        "size": 4531,
        "mode": 420
      },
      {
        "path": "dist/signing/session.js.map",
        "size": 2641,
        "mode": 420
      },
      {
        "path": "dist/tariff.d.ts",
        "size": 919,
        "mode": 420
      },
      {
        "path": "dist/tariff.js",
        "size": 15620,
        "mode": 420
      },
      {
        "path": "dist/tariff.js.map",
        "size": 14199,
        "mode": 420
      },
      {
        "path": "dist/teslafleetapi.d.ts",
        "size": 1818,
        "mode": 420
      },
      {
        "path": "dist/teslafleetapi.js",
        "size": 4570,
        "mode": 420
      },
      {
        "path": "dist/teslafleetapi.js.map",
        "size": 4371,
        "mode": 420
      },
      {
        "path": "dist/teslemetry.d.ts",
        "size": 780,
        "mode": 420
      },
      {
        "path": "dist/teslemetry.js",
        "size": 1288,
        "mode": 420
      },
      {
        "path": "dist/teslemetry.js.map",
        "size": 1128,
        "mode": 420
      },
      {
        "path": "dist/tessie.d.ts",
        "size": 788,
        "mode": 420
      },
      {
        "path": "dist/tessie.js",
        "size": 1025,
        "mode": 420
      },
      {
        "path": "dist/tessie.js.map",
        "size": 1219,
        "mode": 420
      },
      {
        "path": "dist/types/commands.d.ts",
        "size": 1353,
        "mode": 420
      },
      {
        "path": "dist/types/commands.js",
        "size": 271,
        "mode": 420
      },
      {
        "path": "dist/types/commands.js.map",
        "size": 420,
        "mode": 420
      },
      {
        "path": "dist/types/live_status.d.ts",
        "size": 711,
        "mode": 420
      },
      {
        "path": "dist/types/live_status.js",
        "size": 50,
        "mode": 420
      },
      {
        "path": "dist/types/live_status.js.map",
        "size": 123,
        "mode": 420
      },
      {
        "path": "dist/types/products.d.ts",
        "size": 1547,
        "mode": 420
      },
      {
        "path": "dist/types/products.js",
        "size": 47,
        "mode": 420
      },
      {
        "path": "dist/types/products.js.map",
        "size": 117,
        "mode": 420
      },
      {
        "path": "dist/types/responses.d.ts",
        "size": 103,
        "mode": 420
      },
      {
        "path": "dist/types/responses.js",
        "size": 48,
        "mode": 420
      },
      {
        "path": "dist/types/responses.js.map",
        "size": 119,
        "mode": 420
      },
      {
        "path": "dist/types/site_info.d.ts",
        "size": 4317,
        "mode": 420
      },
      {
        "path": "dist/types/site_info.js",
        "size": 48,
        "mode": 420
      },
      {
        "path": "dist/types/site_info.js.map",
        "size": 119,
        "mode": 420
      },
      {
        "path": "dist/types/vehicle_data.d.ts",
        "size": 8344,
        "mode": 420
      },
      {
        "path": "dist/types/vehicle_data.js",
        "size": 51,
        "mode": 420
      },
      {
        "path": "dist/types/vehicle_data.js.map",
        "size": 125,
        "mode": 420
      },
      {
        "path": "dist/types/vehicle.d.ts",
        "size": 521,
        "mode": 420
      },
      {
        "path": "dist/types/vehicle.js",
        "size": 46,
        "mode": 420
      },
      {
        "path": "dist/types/vehicle.js.map",
        "size": 115,
        "mode": 420
      },
      {
        "path": "dist/user.d.ts",
        "size": 778,
        "mode": 420
      },
      {
        "path": "dist/user.js",
        "size": 975,
        "mode": 420
      },
      {
        "path": "dist/user.js.map",
        "size": 754,
        "mode": 420
      },
      {
        "path": "dist/vehicle.d.ts",
        "size": 25815,
        "mode": 420
      },
      {
        "path": "dist/vehicle.js",
        "size": 33779,
        "mode": 420
      },
      {
        "path": "dist/vehicle.js.map",
        "size": 16385,
        "mode": 420
      },
      {
        "path": "dist/vehiclesigned.d.ts",
        "size": 796,
        "mode": 420
      },
      {
        "path": "dist/vehiclesigned.js",
        "size": 1008,
        "mode": 420
      },
      {
        "path": "dist/vehiclesigned.js.map",
        "size": 750,
        "mode": 420
      },
      {
        "path": "dist/vehiclespecific.d.ts",
        "size": 17759,
        "mode": 420
      },
      {
        "path": "dist/vehiclespecific.js",
        "size": 21248,
        "mode": 420
      },
      {
        "path": "dist/vehiclespecific.js.map",
        "size": 11450,
        "mode": 420
      },
      {
        "path": "package.json",
        "size": 773,
        "mode": 420
      },
      {
        "path": "src/charging.ts",
        "size": 2445,
        "mode": 420
      },
      {
        "path": "src/commands.ts",
        "size": 48993,
        "mode": 420
      },
      {
        "path": "src/energy.ts",
        "size": 8010,
        "mode": 420
      },
      {
        "path": "src/energyspecific.ts",
        "size": 5782,
        "mode": 420
      },
      {
        "path": "src/helpers.ts",
        "size": 337,
        "mode": 420
      },
      {
        "path": "src/index.ts",
        "size": 841,
        "mode": 420
      },
      {
        "path": "src/partner.ts",
        "size": 1353,
        "mode": 420
      },
      {
        "path": "src/signing/crypto.ts",
        "size": 1133,
        "mode": 420
      },
      {
        "path": "src/signing/errors.ts",
        "size": 1643,
        "mode": 420
      },
      {
        "path": "src/signing/session.ts",
        "size": 5355,
        "mode": 420
      },
      {
        "path": "src/tariff.ts",
        "size": 18617,
        "mode": 420
      },
      {
        "path": "src/teslafleetapi.ts",
        "size": 5103,
        "mode": 420
      },
      {
        "path": "src/teslemetry.ts",
        "size": 1390,
        "mode": 420
      },
      {
        "path": "src/tessie.ts",
        "size": 1447,
        "mode": 420
      },
      {
        "path": "src/types/commands.ts",
        "size": 1356,
        "mode": 420
      },
      {
        "path": "src/types/live_status.ts",
        "size": 712,
        "mode": 420
      },
      {
        "path": "src/types/products.ts",
        "size": 1554,
        "mode": 420
      },
      {
        "path": "src/types/responses.ts",
        "size": 90,
        "mode": 420
      },
      {
        "path": "src/types/site_info.ts",
        "size": 4320,
        "mode": 420
      },
      {
        "path": "src/types/vehicle_data.ts",
        "size": 8356,
        "mode": 420
      },
      {
        "path": "src/types/vehicle.ts",
        "size": 522,
        "mode": 420
      },
      {
        "path": "src/user.ts",
        "size": 1225,
        "mode": 420
      },
      {
        "path": "src/vehicle.ts",
        "size": 39028,
        "mode": 420
      },
      {
        "path": "src/vehiclesigned.ts",
        "size": 1150,
        "mode": 420
      },
      {
        "path": "src/vehiclespecific.ts",
        "size": 24019,
        "mode": 420
      }
    ],
    "entryCount": 102,
    "bundled": []
  }
]

Pipeline

Updates from git push no-mistakes

✅ **intent** - passed

✅ No issues found.

✅ **Rebase** - passed

✅ No issues found.

🔧 **Review** - 1 issue found → auto-fixed ✅
  • 🚨 .github/workflows/publish.yml:14 - The required CI publish path is not runnable as written. Job-level permissions replaces the workflow-level map, so this job receives id-token: write but not contents: read; consequently actions/checkout cannot access repository contents. Add contents: read to the job permissions, or move id-token: write into the top-level permissions map. This contradicts the intent requirement to “Add a GitHub Actions publish workflow ... so the tesla-fleet-api npm package can be published from CI.”

🔧 Fix: Add checkout permission to publish job
✅ Re-checked - no issues remain.

✅ **Test** - passed

✅ No issues found.

  • npx tsc (initial setup probe; dependencies were absent)
  • pnpm install --frozen-lockfile (workflow-equivalent locked install and prepare build)
  • npx tsc (explicit workflow build)
  • npm pack --dry-run --json with assertions for dist/tariff.js, dist/tariff.d.ts, dist/types/site_info.js, and dist/types/site_info.d.ts
  • Focused .github/workflows/publish.yml contract assertions covering the trigger, OIDC, secret absence, versions, install, build ordering, and publish command
  • git status --short after removing transient dependencies
✅ **Document** - passed

✅ No issues found.

✅ **Lint** - passed

✅ No issues found.

✅ **Push** - passed

✅ No issues found.

Bre77 added 2 commits August 1, 2026 17:33
There is currently no CI publish path: main sits at tesla-fleet-api@0.2.1
with no way to ship it. Restores this repo's own pre-2024 convention
(publish on GitHub Release published) using npm OIDC trusted publishing
instead of a long-lived NPM_TOKEN secret, matching how the rest of the
Teslemetry fleet publishes.
@Bre77
Bre77 merged commit 026df71 into main Aug 1, 2026
1 check passed
@Bre77 Bre77 added the fm Opened by a Firstmate crewmate label Aug 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

fm Opened by a Firstmate crewmate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant